package com.google.code.newpath.core.beanresource;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtils;

import com.fasterxml.jackson.databind.JsonNode;
import com.google.code.newpath.config.BeanResourceConfig;
import com.google.code.newpath.config.ConfigException;
import com.google.code.newpath.core.Context;
import com.google.code.newpath.core.Path;
import com.google.code.newpath.core.Resource;
import com.google.code.newpath.core.ResourceInstanceEvent;
import com.google.code.newpath.core.ResourceInstanceProcessor;
import com.google.code.newpath.core.annotation.ContextIn;
import com.google.code.newpath.core.annotation.ResourceIn;
import com.google.code.newpath.core.annotation.TargetIn;
import com.google.code.newpath.core.exception.ResourceInstanceException;
import com.google.code.newpath.core.exception.SysRuntimeException;
import com.google.code.newpath.util.ReflectionUtils;

public class ConfigInstanceEvent implements ResourceInstanceEvent<Object> {
	
	

	public Object instanceTarget(ResourceInstanceProcessor<Object> processor, Context context, Resource<Object> resource) {

		Object ret = processor.process(context);
		
		weaveProperties(context, ret, resource);
		
		return ret;
	}

	
	protected void weaveProperties(Context context, Object target, Resource<Object> resource) {
		
		Class<?> targetClazz = target.getClass();
		BeanResourceConfig config = (BeanResourceConfig)resource.getConfig();
		if(config == null || config.getProperties() == null) {
			//ignore for noe configuration resource
			return;
		}
		
		Map<String, Object> properties = config.getProperties();
		
		for( Map.Entry<String, Object> entry  : properties.entrySet()) {
			String propName = entry.getKey();
			JsonNode propNode = (JsonNode)entry.getValue();
			if(propNode.isArray()) {
				
			}
			else if(propNode.isObject()) {
				
				//Wired the property value by reference path target.
				JsonNode targetInNode = propNode.get("targetIn");
				if(targetInNode != null) {
					String propPathStr = targetInNode.asText();
					Path propPath = new Path(propPathStr);
					if(propPath.isAbsolute() == false) {
						//Convert propPath to absolute path:  Target ResoucePath + propPath = absolute path
						propPath = new Path(resource.getPath().getParent(), propPathStr);
					}
					
					Object propValue = context.getTarget(propPath, false, false);
					if(propValue != null) {
						try {
							PropertyUtils.setProperty(target, propName, propValue);
						} 
						catch (Exception e) {
							throw new ConfigException("Failed to set property value class='" + target.getClass().getCanonicalName() + "' propertyName='" + propName + "'" , e);
						}
					}
					else {
						throw new ConfigException("Failed to wire property '" + propName + "' path=" + propPath);
					}
				}
			}
			else {
				Class propType;
				try {
					propType = PropertyUtils.getPropertyType(target, propName);
					
					
					if(String.class.isAssignableFrom(propType)) {
						PropertyUtils.setProperty(target, propName, propNode.asText());
					}
					else if((propType == Integer.class) || (propType == Integer.TYPE)) {
						PropertyUtils.setProperty(target, propName, propNode.asInt());
					}
					else if((propType == Boolean.class) || (propType == Boolean.TYPE)) {
						PropertyUtils.setProperty(target, propName, propNode.asBoolean());
					}
					else if((propType == Long.class) || (propType == Long.TYPE)) {
						PropertyUtils.setProperty(target, propName, propNode.asLong());
					}
					else if((propType == Float.class) || (propType == Float.TYPE)) {
						Double doubleValue = propNode.asDouble();
						PropertyUtils.setProperty(target, propName, doubleValue.floatValue());
					}
					else if((propType == Double.class) || (propType == Double.TYPE)) {
						PropertyUtils.setProperty(target, propName, propNode.asDouble());
					}
					
				} 
				catch (Exception e) {
					throw new ConfigException("Failed to set property value class='" + target.getClass().getCanonicalName() + "' propertyName='" + propName + "'" , e);
				}
			}
		}
		
	}
	
	
}
